package com.yuanchu.media.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yuanchu.common.exception.YuanChuException;
import com.yuanchu.media.client.FeignGoodsClient;
import com.yuanchu.media.dao.YoshopUploadFileDao;
import com.yuanchu.media.dto.*;
import com.yuanchu.media.entity.YoshopUploadFileEntity;
import com.yuanchu.media.service.YoshopUploadFileService;
import com.yuanchu.media.utils.FormatUtils;
import io.minio.GetObjectArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.UploadObjectArgs;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.Date;
import java.util.List;

import static com.yuanchu.media.utils.FormatUtils.formatFile;


@Slf4j
@Service("yoshopUploadFileService")
public class YoshopUploadFileServiceImpl extends ServiceImpl<YoshopUploadFileDao, YoshopUploadFileEntity> implements YoshopUploadFileService {

    @Resource
    private FeignGoodsClient feignGoodsClient;

    @Resource
    MinioClient minioClient;
    @Value("${minio.bucket.files}")
    private String bucket_files;
    @Value("${minio.bucket.videofiles}")
    private String bucket_video;
    @Value("${minio.endpoint}")
    private String endpoint;

    @Override
    public PageResult queryFiles(FileQueryParams params) {
        LambdaQueryWrapper<YoshopUploadFileEntity> lqw = new LambdaQueryWrapper<>();
        // 根据文件名查询
        lqw.like(StringUtils.isNotEmpty(params.getFileName()), YoshopUploadFileEntity::getFileName, params.getFileName());
        // 根据分组id查询
        Integer groupId = params.getGroupId();
        if (groupId != null && groupId > 0){
            lqw.eq(YoshopUploadFileEntity::getGroupId, groupId);
        }
        // 根据文件类型查询
        if (params.getFileType() != null && params.getFileType() != -1){
            lqw.eq(YoshopUploadFileEntity::getFileType, params.getFileType());
        }
        // 根据存储类型查询
        lqw.eq(YoshopUploadFileEntity::getStorage, "minio");
        // lqw.eq(StringUtils.isNotEmpty(params.getStorage()), YoshopUploadFileEntity::getStorage, params.getStorage());
        // 根据上传来源
        if (params.getChannel() != null && params.getChannel() != -1){
            lqw.eq(YoshopUploadFileEntity::getChannel, params.getChannel());
        }
        lqw.eq(YoshopUploadFileEntity::getIsDelete, 0);

        Page<YoshopUploadFileEntity> page = new Page<>(params.getPage(), params.getPagesize());
        Page<YoshopUploadFileEntity> page_result = this.page(page, lqw);
        page_result.getRecords().forEach(FormatUtils::formatFile);
        PageResult result = new PageResult();
        result.setTotal(Math.toIntExact(page_result.getTotal()));
        result.setCurrentPage(params.getPage());
        result.setData(page_result.getRecords());
        result.setPerPage(params.getPagesize());
        long total = page_result.getTotal();
        // 计算总页数，向下取整避免超出实际页数（这里已假设pageInfo.getTotal()返回的是long类型）
        int totalPages = (int) Math.ceil((double) total / params.getPagesize());
        result.setLastPage(totalPages);
        return result;

    }

    @Override
    public boolean editFile(EditFileParamsDto params) {
        YoshopUploadFileEntity uploadFile = new YoshopUploadFileEntity();
        BeanUtils.copyProperties(params.getForm(), uploadFile);
        uploadFile.setFileId(params.getFileId());
        return this.updateById(uploadFile);
    }

    @Override
    public boolean deleteFiles(FileIdsDto params) {
        List<Long> fileIds = params.getFileIds();
        // 判断文件id是否关联商品id
        boolean flag = feignGoodsClient.queryMedia(fileIds);
        if (flag) {
            // 存在关联，删除失败
            return false;
        }else {
            // 无关联，删除文件
            return this.removeByIds(fileIds);
        }
    }

    @Override
    public YoshopUploadFileEntity getUploadFileById(Integer imageId) {
        YoshopUploadFileEntity file = this.getById(imageId);
        if (file == null){
            return null;
        }
        return formatFile(file);
    }


    /**
     * 上传文件
     *
     * @param file    文件
     * @param userId  用户id
     * @param channel
     * @return
     */
    @Override
    public YoshopUploadFileEntity uploadFileEntity(MultipartFile file, Integer userId, Integer groupId, int channel) {
        // 构建接受请求类
        UploadFileParamsDto uploadFileParams = new UploadFileParamsDto();
        uploadFileParams.setGroupId(groupId);
        uploadFileParams.setChannel(channel);
        // 文件大小
        uploadFileParams.setFileSize(file.getSize());

        String contentType = file.getContentType();
        // 其他
        if (contentType != null && contentType.contains("image")) {
            // 图片
            uploadFileParams.setFileType(10);
        }else if (contentType != null && contentType.contains("mp4")) {
            // 视频
            uploadFileParams.setFileType(30);
            // 创建临时文件
            File tempFile = null;
            try {
                tempFile = File.createTempFile("minio", "temp");
                // 拷贝文件到临时文件
                file.transferTo(tempFile);
            } catch (IOException e) {
                e.printStackTrace();
                log.error("创建临时文件失败");
                throw new YuanChuException("创建临时文件失败");
            }
            //获取文件绝对路径
            String absolutePath = tempFile.getAbsolutePath();
            uploadFileParams.setAbsolutePath(absolutePath);
            // 文件名称
            uploadFileParams.setFilename(file.getOriginalFilename());
            // 文件类型
            uploadFileParams.setContentType(file.getContentType());
            uploadFileParams.setUploaderId(userId);
            String folder = userId + "/";
            return this.uploadFile(userId, uploadFileParams, null, folder, null);
        }
        // 文件名称
        uploadFileParams.setFilename(file.getOriginalFilename());
        // 文件类型
        uploadFileParams.setContentType(file.getContentType());
        // 文件字节数组
        byte[] bytes = null;
        try {
            bytes = file.getBytes();
        } catch (IOException e) {
            throw new YuanChuException("获取文件字节数组失败");
        }
        uploadFileParams.setUploaderId(userId);
        String folder = userId + "/";
        return this.uploadFile(userId, uploadFileParams, bytes, folder, null);
    }


    @Override
    public List<YoshopUploadFileEntity> getUploadFileByIds(List<Integer> fileIds) {
        List<YoshopUploadFileEntity> file = this.listByIds(fileIds);
        if (file == null){
            return null;
        }
        file.forEach(FormatUtils::formatFile);
        return file;
    }


    @Override
    public YoshopUploadFileEntity uploadFile(Integer uploaderId, UploadFileParamsDto uploadFileParamsDto, byte[] bytes, String folder, String objectName) {
        if (bytes == null){
            bytes = uploadFileParamsDto.getAbsolutePath().getBytes();
        }
        // 上传图片文件使用putObject
        String fileMD5 = DigestUtils.md5DigestAsHex(bytes);

        // 判断数据库文件是否存在
        YoshopUploadFileEntity file = checkFile(fileMD5, uploaderId);
        if (file != null){
            return file;
        }
        // YoshopUploadFileEntity file = this.getOne(new QueryWrapper<YoshopUploadFileEntity>()
        //         .eq("file_md", fileMD5).eq("uploader_id", uploaderId));
        // if (file != null) {
        //     // 文件存在，直接返回
        //     return file;
        // }

        if (StringUtils.isEmpty(folder)) {
            // 文件目录不存在，自动生成目录
            folder = getFileFolder(true, true, true);
        } else if (!folder.endsWith("/")) {
            // 如果目录末尾没有 / ,则添加/
            folder = folder + "/";
        }
        String fileName = uploadFileParamsDto.getFilename();
        // 判断文件名
        if (StringUtils.isEmpty(objectName)) {
            // 如果文件名为空，则设置其默认文件名为文件的md5码 + 文件后缀名
            objectName = fileMD5 + fileName.substring(fileName.lastIndexOf("."));
        }

        // String fileName = objectName;
        // 文件存储地址
        objectName = folder + objectName;
        if (uploadFileParamsDto.getFileType() != 10){
            // 视频或者附件
            YoshopUploadFileEntity uploadFile = uploadFileToMinio(bucket_video, uploadFileParamsDto, objectName, fileMD5, fileName);
            return uploadFile;
        }

        try {
            // 1.上传到minio     * 上传图片文件的通用接口(putObjectArgs方法)
            //      * 适合上传小文件，上传速度快
            ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
            PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                    .bucket(bucket_files)
                    .object(objectName)     // 存储桶里的对象名称。
                    .stream(inputStream, inputStream.available(), -1) // 要上传的流、大小
                    .contentType(uploadFileParamsDto.getContentType())
                    .build();
            minioClient.putObject(putObjectArgs);
            // 2.保持到数据库
            return saveUploadFileDB(uploaderId, uploadFileParamsDto, objectName, fileMD5, fileName, bucket_files);
        } catch (Exception e) {
            e.printStackTrace();
            throw new YuanChuException("上传过程中出错");
        }
    }


    @Override
    public YoshopUploadFileEntity uploadFileToMinio(String bucket, UploadFileParamsDto uploadFileParamsDto, String objectName, String fileMD5, String fileName) {
        try {
            UploadObjectArgs uploadObjectArgs = UploadObjectArgs.builder()
                    .bucket(bucket)
                    .object(objectName)
                    .filename(uploadFileParamsDto.getAbsolutePath())
                    .contentType(uploadFileParamsDto.getContentType())
                    .build();
            minioClient.uploadObject(uploadObjectArgs);
            log.info("上传文件到 minio 成功,bucket:{},objectName:{}", bucket, objectName);
            // 2.保持到数据库
            return saveUploadFileDB(uploadFileParamsDto.getUploaderId(), uploadFileParamsDto, objectName, fileMD5, fileName, bucket);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("上传文件到 minio 出错,bucket:{},objectName:{},错误原因:{}", bucket, objectName, e.getMessage(), e);
            throw  new YuanChuException("上传文件到文件系统失败");
        }
    }


    @Override
    public YoshopUploadFileEntity checkFile(String fileMd5, Integer uploaderId) {
        //查询文件信息
        YoshopUploadFileEntity mediaFiles = this.getOne(new QueryWrapper<YoshopUploadFileEntity>()
                .eq("file_md", fileMd5).eq("uploader_id", uploaderId));
        if (mediaFiles != null) {
            //桶
            String bucket = mediaFiles.getBucket();
            //存储目录
            String filePath = mediaFiles.getFilePath();
            //文件流
            InputStream stream = null;
            try {
                stream = minioClient.getObject(
                        GetObjectArgs.builder()
                                .bucket(bucket)
                                .object(filePath)
                                .build());

                if (stream != null) {
                    //文件已存在
                    return mediaFiles;
                }
            } catch (Exception e) {

            }
        }

        //文件不存在
        return null;
    }
    @Transactional
    public YoshopUploadFileEntity saveUploadFileDB(Integer uploaderId, UploadFileParamsDto uploadFileParamsDto, String objectName, String fileMD5, String fileName, String bucket) {
        YoshopUploadFileEntity fileEntity = this.getById(fileMD5);
        if (fileEntity == null) {
            fileEntity = new YoshopUploadFileEntity();
            BeanUtils.copyProperties(uploadFileParamsDto, fileEntity);
            fileEntity.setUploaderId(uploaderId);
            fileEntity.setFileMd(fileMD5);
            // 上传来源(10商户后台 20用户端)
            fileEntity.setChannel(uploadFileParamsDto.getChannel());
            fileEntity.setStorage("minio");
            fileEntity.setDomain(endpoint+ "/");
            fileEntity.setBucket(bucket);
            // 文件类型(10图片 20附件 30视频)
            fileEntity.setFileType(uploadFileParamsDto.getFileType());
            fileEntity.setFileExt(uploadFileParamsDto.getContentType());
            fileEntity.setFileName(fileName);
            fileEntity.setFilePath(bucket + "/" + objectName);
            fileEntity.setFileSize(uploadFileParamsDto.getFileSize());
            fileEntity.setStoreId(10001);
            fileEntity.setGroupId(uploadFileParamsDto.getGroupId());
            fileEntity.setCreateTime(String.valueOf(Instant.now().toEpochMilli() / 1000));
            fileEntity.setUpdateTime(String.valueOf(Instant.now().toEpochMilli() / 1000));
        }
        boolean save = this.save(fileEntity);
        if (!save) {
            throw new YuanChuException("保存文件信息失败");
        }
        fileEntity.setPreviewUrl(fileEntity.getDomain() + fileEntity.getFilePath());
        fileEntity.setExternalUrl(fileEntity.getDomain() + fileEntity.getFilePath());
        return fileEntity;
    }


    /**
     * 自动生成目录
     * @param year  是否包含年
     * @param month 是否包含月
     * @param day   是否包含日
     * @return
     */
    private String getFileFolder(boolean year, boolean month, boolean day) {
        StringBuffer stringBuffer = new StringBuffer();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        String dateString = dateFormat.format(new Date());
        String[] split = dateString.split("-");
        if (year) {
            stringBuffer.append(split[0]).append("/");
        }
        if (month) {
            stringBuffer.append(split[1]).append("/");
        }
        if (day) {
            stringBuffer.append(split[2]).append("/");
        }
        return stringBuffer.toString();
    }
    /**
     * 获取文件默认存储目录路径 年/月/日
     */
    private String getFileFolder() {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        String date = dateFormat.format(new Date()).replace("-", "/") + "/";
        return date;
    }
}